/**
 * 给一个树，1是树根。
 * 选任意节点，将该子树挂到任意其他位置
 * 如此操作一次后的最小直径是多少。也可以不操作。
 * 
 * 首先计算u到儿子的最长、次长、三长，记作Lu，注意后二者可能不存在。一遍DFS。
 * (也许L初始化为{1,1,1}即可无缝解决不存在的情况)
 * 
 * 然后计算u子树的直径，这需要计算Fu，Fu表示u的父节点走其他路能达到的最长
 * 令u的父亲为p， 则 Fu = max(Fp, Lp0)。
 * 令Du是u子树的直径，则 Du = max(Lu0+Lu1-1, max{Dv, v是u的儿子})
 * 注意次长不存在的情况
 * 一遍DFS
 * 
 * 最后计算u节点的外径，记作Wu。即去掉u子树，剩下的树的直径
 * u的父节点记作p，则Wu可能有三个来源：
 * Wu = max(Wp, max{Dv, v是p除了u之外的其他儿子}, 经过p的路径)
 * 其中经过p的路径又有几种可能：
 * 当u是p的最长链，则为: max(Fp + Lp1, Lp1 + Lp2)
 * 当u是p的次长链，则为：max(Fp + Lp0, Lp0 + Lp2)
 * 其他则为： max(Fp + Lp0, Lp0 + Lp1)
 * 
 * 最后基于贪心，当某个子树拆下来接在哪里最合适？显然要接在剩下树的直径的一半处。
 * 这样形成的直径肯定是最短。如果树有多个直径，其中点必然重合。
 * 
 * 所以for每个节点i：
 *   新树的直径最小可能是 max(Wi, Di, Li0 + 1 + Wi/2)
 * 所有新树直径中最小的即为答案
 */
#include <bits/stdc++.h>
using namespace std;

#include <bits/extc++.h>
using namespace __gnu_pbds;

using llt = long long;
using Real = double;
using vi = vector<int>;

int N;
vector<vi> G;

vi Hson;
/// Lu是u到儿子的最长、次长和第三长
/// 最长至少是1，后二者可能没有，记作0
vector<array<int, 3>> L;
/// Fu表示u的父节点往外的最长
/// Du表示u子树的直径
vi F, D;
/// Wu表示除去u子树剩下的树的直径
vi W;

void dfs_lian(int u, int p){
    if(1 == G[u].size() && 1 != u){
        return (void)(L[u] = {1, 0, 0});
    }

    auto __update = [](array<int, 3> & a, int & h, int v, int lian){
        if(a[0] < lian){ // 要判断最长、次长的存在性
            if(a[1]) a[2] = a[1];
            if(a[0]) a[1] = a[0];
            a[0] = lian; 
            h = v;
        }else if(a[1] < lian){
            if(a[1]) a[2] = a[1]; 
            a[1] = lian;
        }else if(a[2] < lian){
            a[2] = lian;
        }
    };

    auto & a = L[u];
    auto & h = Hson[u];

    for(auto v : G[u]){
        if(v == p) continue;

        dfs_lian(v, u);
        __update(a, h, v, L[v][0] + 1);
    }

    return;
}

void dfs_diam(int u, int p){
    int d = 0;
    auto fu = F[u];
    auto mx = L[u][0], se = L[u][1];

    for(auto v : G[u]){
        if(v == p) continue;
        /// 站在u的角度计算v
        if(v == Hson[u]){
            auto tmp = max(se, fu);
            F[v] = max(fu + 1, tmp);
            dfs_diam(v, u);
        }else{
            auto tmp = max(mx, fu);
            F[v] = max(fu + 1, tmp);
            dfs_diam(v, u);
        }
        /// d是所有子树的最大直径
        d = max(d, D[v]);   
    }

    /// 注意判断次长的存在性
    if(se) D[u] = max(mx + se - 1, d);
    else D[u] = max(mx, d);
    return;
}

int flina(int u, int v){
    int sz = G[u].size();
    if(u != 1) sz -= 1;

    if(1 == sz) return 1;
    if(2 == sz){
        if(v == Hson[u]) {
            return L[u][1];
        } 
        return L[u][0];
    }

    if(Hson[u] == v){
        return L[u][1] + L[u][2] - 1;
    } 
    if(L[v][0] + 1 == L[u][1]){
        return L[u][0] + L[u][2] - 1;
    } 
    return L[u][0] + L[u][1] - 1;        
}

void dfs_wai(int u, int p){
    /// 首先求出u的儿子中最大直径和次大直径
    int h1 = 0, h2 = 0;
    for(auto v : G[u]){
        if(v == p) continue;

        auto tmp = D[v]; 
        if(0 == h1 or D[h1] < tmp){
            h2 = h1; h1 = v;
        }else if(0 == h2 or D[h2] < tmp){
            h2 = v;
        }
    }

    for(auto v : G[u]){
        if(v == p) continue;

        /// 站在u的角度计算v
        /// 取除去v儿子其他儿子的最大直径
        auto tmp = D[h1];
        if(v == h1) tmp = D[h2];
        
        /// 取外径与其他儿子中更大的
        tmp = max(tmp, W[u]);

        /// 从父节点过来的向下延伸的最长路径
        auto t2 = F[u];
        if(Hson[u] == v) t2 += (L[u][1] ? L[u][1] : 1);
        else t2 += L[u][0];  
   
        tmp = max(tmp, t2);

        /// u的两个子链构成的最长路径，注意次长与三长的存在性     
        t2 = flina(u, v);
        tmp = max(tmp, t2);
        W[v] = tmp;

        dfs_wai(v, u);
    }

    return;
}

int proc(){
    Hson.assign(N + 1, 0);
    L.assign(N + 1, {0, 0, 0});

    dfs_lian(1, 0);

    // for(int i=1;i<=N;++i){
    //     cout << i << ": " << "max = " << L[i][0] << ", second = " << L[i][1] << ", hson = " << Hson[i] << "\n";
    // }

    F.assign(N + 1, 0);
    D.assign(N + 1, 0);
    dfs_diam(1, 0);
    // for(int i=1;i<=N;++i){
    //     cout << i << ": " << "from parent = " << F[i] << ", dia = " << D[i] << "\n";
    // }

    W.assign(N + 1, 0);
    dfs_wai(1, 0);
    // for(int i=1;i<=N;++i){
    //     cout << i << ": " << "wai dia = " << W[i] << "\n";
    // }

    int ans = N;
    for(int i=1;i<=N;++i){
        auto tmp = max(W[i], D[i]);
        if(i != 1) tmp = max(tmp, W[i] / 2 + 1 + L[i][0]);
        if(tmp < ans){
            ans = tmp;
            // cout << "ans = " << i << endl;
        }
    }

    return ans;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(0);
    int nofkase = 1;
    cin >> nofkase;
    while(nofkase--){
        cin >> N;
        G.assign(N + 1, {});
        for(int a,b,i=1;i<N;++i){
            cin >> a >> b;
            G[a].push_back(b);
            G[b].push_back(a);
        }
        cout << proc() << "\n";
    }
    return 0;
}